#include "Map.h"
#include <iostream>

using std::string;
using std::vector;
using std::cout;
using std::endl;
using std::ifstream;
using std::cin;



Map::Map(std::string lvl)
{
	width = 50;													// default width and length
	length = 50;

	//loadMapFromFile("maps\\level1.txt");
	loadMapFromFile("maps\\"+lvl);						// loading the txt map
	
	int longest = (width >= length) ? width : length;			// longest dimension

	initArrays();

	// position of bases
	homeBaseX = 8;
	homeBaseZ = length - 1;
	enemyBaseX = width - 8;
	enemyBaseZ = 1;
	nuclearFactoryX = 2;
	nuclearFactoryZ = 32;
	missileFactoryX = 2;
	missileFactoryZ = 27;
	cannonFactoryX = 2;
	cannonFactoryZ = 22;
	eBrainFactoryX = 6;
	eBrainFactoryZ = 29;
	phaserFactoryX = 6;
	phaserFactoryZ = 24;

	// initializing members
	regularTile =  new Tile();
	mRubble = new MediumRubble();
	lRubble = new LightRubble();
	largeRubble = new LargeRubble();
	midPit = new MiddlePit();
	endPit = new EndPit();
	delimiter = new PlatformDelimiter();
	skybox = new skyBox(longest*2 + 20, width/2, 0, length/2);	// adjust to map dimensions
	homebase = new HomeBase();
	enemyBase = new HomeBase(1);
	nuclearFactory = new IntermediateBase(0);
	missileFactory = new IntermediateBase(1);
	cannonFactory = new IntermediateBase(2);
	eBrainFactory = new IntermediateBase(3);
	phaserFactory = new IntermediateBase(4);
	
	constBlock = new PlainBlock();
	constBlockShort = new PlainShortBlock();
	holeBlock = new HoleBlock();
	holeBlockShort = new ShortHoleBlock();

	// lightposts
	lightPostNW = new lightPost(1, 0,0,0);
	lightPostNE = new lightPost(2, width, 0,0);
	lightPostSW = new lightPost(3, 0,0,length);
	lightPostSE = new lightPost(4, width,0,length);

	// make the display list
	makeDisplayList();

	setMapFixedObject();
}

void Map::initArrays()
{
	int i;
	int j;
	movableArray.resize(width);
	fixedArray.resize(width);
	for(i=0; i<width; ++i)
	{
		movableArray[i].resize(length);
		fixedArray[i].resize(length);
	}

	for(i=0; i<width; ++i)
	{
		for(j=0; j<length; ++j)
		{
			Element *e = new Element;
			e->ID = 0;
			e->height = 0;
			e->y = 0;
			movableArray[i][j] = e;
			Element *e2 = new Element;
			e2->ID = 0;
			e2->height = 0;
			e2->y = 0;
			fixedArray[i][j] = e2;
		}
	}
}

void Map::setMapFixedObject()
{
	for(int i =0; i<mapArray.size(); ++i)
	{
		for(int j = 0; j<mapArray[i].size(); ++j)
		{
			char letter = mapArray[i][j];
			switch(letter)
			{
				case 't':
					if(j==0 || i==0 || j==mapArray[i].size()-1 || i==mapArray.size()-1)
					{
						fixedArray[i][j]->ID = -1;
						fixedArray[i][j]->height = 1;
					}
					break;
				case 'l':
					fixedArray[i][j]->ID = -2;
					//fixedArray[i][j]->height = ;
					break;
				case 'L':
					fixedArray[i][j]->ID = -2;
					//fixedArray[i][j]->height = ;
					break;	
				case 'm':
					fixedArray[i][j]->ID = -2;
					//fixedArray[i][j]->height = ;
					break;
				case 'p':
					fixedArray[i][j]->ID = -3;
					break;
				case 'e':
					fixedArray[i][j]->ID = -4;
					break;
				case 'f':
					fixedArray[i][j]->ID = -4;
					break;
				case 'b':
					fixedArray[i][j]->ID = -5;
					fixedArray[i][j]->height = 1;
					break;
				case 'B':
					fixedArray[i][j]->ID = -5;
					fixedArray[i][j]->height = 1;
					break;
				case 's':
					fixedArray[i][j]->ID = -5;
					fixedArray[i][j]->height = 0.5;
					break;
				case 'S':
					fixedArray[i][j]->ID = -5;
					fixedArray[i][j]->height = 0.5;
					break;
			}
		}
	}
	
	// bases
	int x, z;
	for (x = homeBaseX - 6; x < homeBaseX - 1; ++x)
		for(z = homeBaseZ - 5; z < homeBaseZ - 1; ++z)
		{
			fixedArray[x][z]->ID = -6;
			fixedArray[x][z]->height = 1;
		}
	for (x = enemyBaseX + 1; x < enemyBaseX + 6; ++x)
		for(z = enemyBaseZ + 1; z < enemyBaseZ + 5; ++z)
		{
			fixedArray[x][z]->ID = -6;
			fixedArray[x][z]->height = 1;
		}
	for (x = nuclearFactoryX + 1; x < nuclearFactoryX + 3; ++x)
		for(z = nuclearFactoryZ - 4; z < nuclearFactoryZ - 1; ++z)
		{
			fixedArray[x][z]->ID = -6;
			fixedArray[x][z]->height = 1;
		}
	for (x = missileFactoryX + 1; x < missileFactoryX + 3; ++x)
		for(z = missileFactoryZ - 4; z < missileFactoryZ - 1; ++z)
		{
			fixedArray[x][z]->ID = -6;
			fixedArray[x][z]->height = 1;
		}
	for (x = cannonFactoryX + 1; x < cannonFactoryX + 3; ++x)
		for(z = cannonFactoryZ - 4; z < cannonFactoryZ - 1; ++z)
		{
			fixedArray[x][z]->ID = -6;
			fixedArray[x][z]->height = 1;
		}
	for (x = eBrainFactoryX + 1; x < eBrainFactoryX + 3; ++x)
		for(z = eBrainFactoryZ - 4; z < eBrainFactoryZ - 1; ++z)
		{
			fixedArray[x][z]->ID = -6;
			fixedArray[x][z]->height = 1;
		}
	for (x = phaserFactoryX + 1; x < phaserFactoryX + 3; ++x)
		for(z = phaserFactoryZ - 4; z < phaserFactoryZ - 1; ++z)
		{
			fixedArray[x][z]->ID = -6;
			fixedArray[x][z]->height = 1;
		}

		fixedArray[nuclearFactoryX + 1][nuclearFactoryZ - 3]-> height = 1.3;
		fixedArray[missileFactoryX + 1][missileFactoryZ - 3]-> height = 1.2;
		fixedArray[cannonFactoryX + 1][cannonFactoryZ - 3]-> height = 1.6;
		fixedArray[phaserFactoryX + 1][phaserFactoryZ - 3]-> height = 1.6;
		fixedArray[eBrainFactoryX + 1][eBrainFactoryZ - 3]-> height = 2.6;
}

// populates 
void Map::loadMapFromFile(string levelFile)
{
	// vars
	ifstream fin;
	char buffer;
	//string::size_type index;
	int index = 0;

	// opening the file
	fin.open(levelFile.c_str());
	if(!fin)
	{
		cout << "Error opening the file " << levelFile << endl;
	}

	// reading from the file to the vector
	fin >> std::noskipws;
	while(fin >> buffer)
	{
		if(buffer == '\n')
		{
			index = 0;
		}
		else if(buffer != ' ')
		{
			if(index >= mapArray.size())
				mapArray.resize(index + 1);

			int s = mapArray[index].size();
			mapArray[index].push_back(buffer);
			mapArray[index].resize(s + 1);
			++index;
		}
	}

	// set the platform dimensions based on the level's
	length = mapArray[0].size();
	width = mapArray.size();
}

void Map::makeDisplayList()
{
	// create a new display list
	DLindex = glGenLists(1);
	glNewList(DLindex, GL_COMPILE);

		// align and level the map with the x,z plane
		glPushMatrix();
			glTranslatef(0.5, -0.5, 0.5);

			// begin drawing the map
			for(unsigned int i=0; i < mapArray.size(); ++i)
			{
				for(unsigned int k=0; k<mapArray[i].size(); ++k)
				{
					glPushMatrix();
					glTranslatef(i, 0, k); // move tile the number of units needed

					char letter = mapArray[i][k];

					// draw the type of tile according to the letter stored in 
					// the correspoding array position
					switch(letter)
					{
						case 't':
						{
							// regular tile

							// put a PlatformDelimiter object on top if it is a tile on the edge of the map
							if(k==0 || i==0 || k==mapArray[i].size()-1 || i==mapArray.size()-1)
							{
								delimiter->display();
								regularTile->display();
								//fixedArray[i][k]->ID = -1;
							}
							else
							{
								// draw a tile with team number and without a delimiter
								regularTile->display();
							}
							break;
						}
						case 'l':
							// light rubble
							lRubble->display();
							//fixedArray[i][k]->ID = -2;
							//fixedArray[i][k]->height = ;
							break;
						case 'L':
							// Large rubble
							largeRubble->display();
							//fixedArray[i][k]->ID = -2;
							//fixedArray[i][k]->height = ;
							break;	
						case 'm':
							// medium rubble
							mRubble->display();
							//fixedArray[i][k]->ID = -2;
							//fixedArray[i][k]->height = ;
							break;
						case 'p':
							// medium pit
							midPit->display();
							//fixedArray[i][k]->ID = -3;
							break;
						case 'e':
							// ending pit
							endPit->display();
							//fixedArray[i][k]->ID = -4;
							break;
						case 'f':
							// ending pit rotated
							glPushMatrix();
							  glRotatef(180, 0,1,0);
							  endPit->display();
							glPopMatrix();
							//fixedArray[i][k]->ID = -4;
							break;
						case 'b':
							// construction block
							constBlock->display();
							regularTile->display();
							//fixedArray[i][k]->ID = -5;
							break;
						case 'B':
							// construction block with hole
							holeBlock->display();
							regularTile->display();
							//fixedArray[i][k]->ID = -5;
							break;
						case 's':
							// construction block short
							constBlockShort->display();
							regularTile->display();
							//fixedArray[i][k]->ID = -5;
							break;
						case 'S':
							// construction block short with hole
							holeBlockShort->display();
							regularTile->display();
							//fixedArray[i][k]->ID = -5;
							break;

					}					
					glPopMatrix();
				}
			}
		
		glPopMatrix();

		glPushMatrix();
			skybox->draw();
		glPopMatrix();
		
		// adding the home base
		glPushMatrix();
			glTranslatef(homeBaseX, 0, homeBaseZ);
			glRotatef(180, 0,1,0);
			homebase->display();
		glPopMatrix();

		// adding the enemy base
		glPushMatrix();
			glTranslatef(enemyBaseX, 0, enemyBaseZ);
			enemyBase->display();
		glPopMatrix();

		// adding the nuclear base
		glPushMatrix();
			glTranslatef(nuclearFactoryX, 0, nuclearFactoryZ);
			glRotatef(90, 0,1,0);
			nuclearFactory->display();		
		glPopMatrix();

		// adding the missile base
		glPushMatrix();
			glTranslatef(missileFactoryX, 0, missileFactoryZ);
			glRotatef(90, 0,1,0);
			missileFactory->display();
		glPopMatrix();

		// adding the cannon base
		glPushMatrix();
			glTranslatef(cannonFactoryX, 0, cannonFactoryZ);
			glRotatef(90, 0,1,0);
			cannonFactory->display();
		glPopMatrix();

		// adding the eBrain base
		glPushMatrix();
			glTranslatef(eBrainFactoryX, 0, eBrainFactoryZ);
			glRotatef(90, 0,1,0);
			eBrainFactory->display();
		glPopMatrix();

		// adding the phaser base
		glPushMatrix();
			glTranslatef(phaserFactoryX, 0, phaserFactoryZ);
			glRotatef(90, 0,1,0);
			phaserFactory->display();
		glPopMatrix();

	glEndList();

	// since we put the rendering in a display list, we don't need these anymore
	delete regularTile;
	delete mRubble;
	delete lRubble;
	delete largeRubble;
	delete midPit;
	delete endPit;
	delete delimiter;
	delete skybox;
}

GLint Map::getWidth()
{
	return width;
}

GLint Map::getLength()
{
	return length;
}

GLint Map::getSkyBoxSize()
{
	return skybox->getSize();
}

void Map::display()
{
	glPushMatrix();
		glCallList(DLindex);
	glPopMatrix();

		//adding lightpost 1
		glPushMatrix();
			lightPostNW->display();
			
		glPopMatrix();

		//adding lightpost 2
		glPushMatrix();
			lightPostNE->display();
		glPopMatrix();

		//adding lightpost 3
		glPushMatrix();
			lightPostSW->display();
		glPopMatrix();

		//adding lightpost 4
		glPushMatrix();
			lightPostSE->display();
		glPopMatrix();
}

int Map::moveControlUnit(GLfloat newX, GLfloat newY, GLfloat newZ)
{
	if(newX >= width || newX < 0 || newZ >= length || newZ < 0 || newY < 0)
	{
		// out of bounds
		return -1;
	}

	int id = checkInFixedObject(newX, newY, newZ);
	if(!id)
	{
		if(movableArray[newX][newZ]->ID)
		{
			if (movableArray[newX][newZ]->height + 0.09 >= newY && movableArray[newX][newZ]->height - 0.09 <= newY)
				return movableArray[newX][newZ]->ID;
			else if(movableArray[newX][newZ]->height < newY)
				return 0;
			else
				return -1;
		}
		return 0;
	}
	else
	{
		return -1;
	}
}

int Map::move(GLfloat newX, GLfloat newY, GLfloat newZ, GLfloat oldX, GLfloat oldY, GLfloat oldZ)
{
	if(newX >= width || newX < 0 || newZ >= length || newZ < 0 || newY < 0)
	{
		// out of bounds
		return -1;
	}

	int id = checkInFixedObject(newX, newY, newZ);
	if(!id)
		return checkInMovableObject(newX, newY, newZ, oldX, oldY, oldZ);
	else
	{
		return -1;
	}
}

int Map::checkInFixedObject(GLfloat newX, GLfloat newY, GLfloat newZ)
{
	if(fixedArray[newX][newZ]->height < newY)
		return 0;
	return fixedArray[newX][newZ]->ID;
}

int Map::checkInMovableObject(GLfloat newX, GLfloat newY, GLfloat newZ, GLfloat oldX, GLfloat oldY, GLfloat oldZ)
{
	// if the object wants to go up
	if(newX == oldX && newZ == oldZ)
	{
		movableArray[newX][newZ]->y = newY;
		return 0;
	}
	else if(!movableArray[newX][newZ]->ID)
	{
		if(oldX != -1 && oldY != -1 && oldZ != -1)
		{
			movableArray[newX][newZ]->ID = movableArray[oldX][oldZ]->ID;
			movableArray[newX][newZ]->height = movableArray[oldX][oldZ]->height;
			movableArray[newX][newZ]->y = movableArray[oldX][oldZ]->y;
			movableArray[oldX][oldZ]->ID = 0;
			movableArray[oldX][oldZ]->height = 0;
			movableArray[oldX][oldZ]->y = 0;
		}
		return 0;
	}
	else
	{
		return movableArray[newX][newZ]->ID;
	}
}

void Map::addIDmovableObject(int id)
{
	IDmovableObjects.push_back(id);
}

void Map::removeIDmovableObject(int id)
{
	
}

void Map::setLocation(int id, GLfloat x, GLfloat y, GLfloat z, GLfloat h)
{
	bool find = false;
	int i;
	for(i=0; i<IDmovableObjects.size(); ++i)
	{
		if(IDmovableObjects[i] == id)
			find = true;
	}
	if(!find)
		addIDmovableObject(i);
	
	movableArray[x][z]->ID = id;
	movableArray[x][z]->y = y;
	movableArray[x][z]->height = h;
}

void Map::removeObject(GLfloat oldX, GLfloat oldZ)
{
	//std::cout << "remove robot " << movableArray[oldX][oldZ]->ID << std::endl;
	movableArray[oldX][oldZ]->ID = 0;
	movableArray[oldX][oldZ]->height = 0;
	movableArray[oldX][oldZ]->y = 0;

	//removeIDmovableObject();
}

Map::~Map(void)
{
	delete lightPostNW;
	delete lightPostNE;
	delete lightPostSW;
	delete lightPostSE;
}
